package edu.pitt.cs.cs2410pj2.core;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;

import edu.pitt.cs.cs2410pj2.datamodel.MessageBus;
import edu.pitt.cs.cs2410pj2.util.Parameters;
import edu.pitt.cs.cs2410pj2.util.TheLogger;
import edu.pitt.cs.cs2410pj2.util.Trace;

public class CMP {
	private ArrayList<Tile> tiles;
	private MessageBus msgBus;
	private ArrayList<Trace> traces;
	int clock = 0;
	private TheLogger logger;

	public void setLogger(TheLogger logger) {
		this.logger = logger;
	}

	public CMP(Parameters para) {
		tiles = new ArrayList<Tile>();
		msgBus = new MessageBus(tiles, para);
		int numP = (int) Math.pow(2, para.p);
		for (int i = 0; i < numP; i++) {
			Tile t = new Tile(para, i);
			t.setBus(msgBus);
			tiles.add(t);
		}
	}

	public void showLog() throws IOException {
		logger.log("===============CMP=================");
		for (int i = 0; i < tiles.size(); i++) {
			tiles.get(i).showLog(logger);
		}
		logger.log("*****MESSAGE******");
		logger.log(msgBus.toString());
	}

	// Printing out information of all the traces in the end
	public void showFinalTrace() throws IOException {
		logger.log("**********Final logs of traces****************");
		for (int i = 0; i < traces.size(); i++) {
			logger.log(traces.get(i).toString());
		}
	}

	public void showFinalStatistics() throws IOException {
		logger.log("\n*********The hit/miss rate of each tile*************");
		for (int i = 0; i < tiles.size(); i++) {
			logger.log("TILE " + i + ",HIT:" + tiles.get(i).getHit() + ",MISS:"
					+ tiles.get(i).getMissCount() + ",HIT MISS RATIO: "
					+ tiles.get(i).getHitMissRate());
		}
		logger.log("\n*********Miss Penalty*************");
		for (int i = 0; i < tiles.size(); i++) {
			logger.log("TILE " + i);
			for (int j = 0; j < traces.size(); j++) {
				if (traces.get(j).getCordId() == i) {
					logger.log("[" + traces.get(j).toString() + "]");
					logger.log("Miss penalty:" + traces.get(j).getMissPenalty());
				}
			}
		}
		logger.log("\n********Messages received************");
		for (int i = 0; i < tiles.size(); i++) {
			logger.log("TILE " + i);
			logger.log(tiles.get(i).getMsgInfo());
		}
	}

	public void addTraces(ArrayList<Trace> traces) {
		this.traces = traces;
	}

	public void run(boolean isDebug) throws IOException {
		boolean isFinished = false;
		int counter = 0;
		while (!isFinished) {
			for (int i = counter; i < traces.size(); i++) {
				if (traces.get(i).getCycle() == clock) {
					int tileId = traces.get(i).getCordId();
					tiles.get(tileId).addTrace(traces.get(i));
					counter++;
				}
			}
			// tiles issuing the traces
			for (int i = 0; i < tiles.size(); i++) {
				tiles.get(i).issue(clock);
			}
			
			msgBus.tick();

			if (isDebug) {
				logger.log("CLOCK:" + clock);
				showLog();
			}
			logger.log("CLOCK:"+clock);
			showLog();
			isFinished = true;
			for (int i = 0; i < traces.size(); i++) {
				if (!traces.get(i).isFinished()) {
					isFinished = false;
					break;
				}
			}
			if (!isFinished)
				clock++;
		}
		logger.log("^^^^^^^^^^^^^^ALL TILES FINISHED AT CLOCK CYCLE:" + clock
				+ "^^^^^^^^^^^^^");
		showFinalTrace();
		showFinalStatistics();
	}
}
